"""
认证服务 API
所有接口采用 POST 方法，统一响应格式
"""
from fastapi import APIRouter, Depends, Request, HTTPException
from motor.motor_asyncio import AsyncIOMotorDatabase
from datetime import datetime
from pydantic import BaseModel, EmailStr, Field
from typing import Optional
from bson import ObjectId

from database.connection import get_database
from models.user import UserCreate, UserOut
from models.password import PasswordChange, EmailChange
from services.auth import hash_password, verify_password, create_access_token, create_refresh_token
from api.deps_v1 import get_current_user
from utils.response import success, error
from models.response import ErrorCode

router = APIRouter(prefix="/sac/v1/Auth", tags=["认证服务"])

# 服务名称
SERVICE_NAME = "Auth"


# ========== 请求模型 ==========

class RegisterRequest(BaseModel):
    """注册请求"""
    email: Optional[EmailStr] = None
    username: str = Field(..., min_length=3, max_length=50)
    password: str = Field(..., min_length=8)


class LoginRequest(BaseModel):
    """登录请求"""
    username: str = Field(..., description="用户名或邮箱")
    password: str = Field(..., min_length=8)


class ChangePasswordRequest(BaseModel):
    """修改密码请求"""
    old_password: str
    new_password: str = Field(..., min_length=8)


class ChangeEmailRequest(BaseModel):
    """修改邮箱请求"""
    new_email: EmailStr


# ========== 响应模型 ==========

class RegisterResult(BaseModel):
    """注册结果"""
    user: UserOut


class LoginResult(BaseModel):
    """登录结果"""
    access_token: str
    token_type: str = "bearer"
    refresh_token: Optional[str] = None
    expires_in: int = 28800  # 8小时


class ProfileResult(BaseModel):
    """用户信息结果"""
    user: UserOut


class ChangePasswordResult(BaseModel):
    """修改密码结果"""
    message: str
    token_invalidated: bool = True


class ChangeEmailResult(BaseModel):
    """修改邮箱结果"""
    message: str
    new_email: str


# ========== API 接口 ==========

@router.post("/Register", summary="用户注册")
async def register(
    request: Request,
    body: RegisterRequest,
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    用户注册
    
    - **email**: 邮箱地址（普通用户必填）
    - **username**: 用户名（3-50字符）
    - **password**: 密码（至少8位）
    """
    try:
        # 普通用户注册必须提供邮箱
        if not body.email:
            return error(
                request, "Register", SERVICE_NAME,
                ErrorCode.INVALID_EMAIL,
                detail="普通用户注册时邮箱不能为空"
            )
        
        # 检查用户名和邮箱是否已存在
        query_conditions = [{"username": body.username}]
        if body.email:
            query_conditions.append({"email": body.email})
        
        existing_user = await db.users.find_one({"$or": query_conditions})
        
        if existing_user:
            if body.email and existing_user.get("email") == body.email:
                return error(
                    request, "Register", SERVICE_NAME,
                    ErrorCode.EMAIL_ALREADY_EXISTS
                )
            else:
                return error(
                    request, "Register", SERVICE_NAME,
                    ErrorCode.USERNAME_ALREADY_EXISTS
                )
        
        # 加密密码
        hashed = hash_password(body.password)
        
        # 构建用户数据
        user_data = {
            "email": body.email,
            "username": body.username,
            "hashed_password": hashed,
            "role": "user",
            "disabled": False,
            "created_at": datetime.utcnow(),
            "updated_at": None
        }
        
        # 插入数据库
        result = await db.users.insert_one(user_data)
        
        # 查询新创建的用户
        new_user = await db.users.find_one({"_id": result.inserted_id})
        new_user["id"] = str(new_user.pop("_id"))
        
        return success(
            request, "Register", SERVICE_NAME,
            RegisterResult(user=UserOut(**new_user)).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "Register", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/Login", summary="用户登录")
async def login(
    request: Request,
    body: LoginRequest,
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    用户登录
    
    支持使用用户名或邮箱登录
    """
    try:
        # 查找用户（支持用户名或邮箱）
        user = await db.users.find_one({
            "$or": [
                {"username": body.username},
                {"email": body.username}
            ]
        })
        
        if not user:
            return error(
                request, "Login", SERVICE_NAME,
                ErrorCode.USER_NOT_FOUND,
                message="用户名/邮箱或密码错误"
            )
        
        # 验证密码
        if not verify_password(body.password, user["hashed_password"]):
            return error(
                request, "Login", SERVICE_NAME,
                ErrorCode.INVALID_PASSWORD,
                message="用户名/邮箱或密码错误"
            )
        
        # 检查用户是否被禁用
        if user.get("disabled", False):
            return error(
                request, "Login", SERVICE_NAME,
                ErrorCode.USER_DISABLED
            )
        
        # 生成 Token
        token_data = {
            "sub": str(user["_id"]),
            "username": user["username"],
            "email": user.get("email", ""),
            "role": user.get("role", "user")
        }
        
        access_token = create_access_token(token_data)
        refresh_token = create_refresh_token(token_data)
        
        return success(
            request, "Login", SERVICE_NAME,
            LoginResult(
                access_token=access_token,
                refresh_token=refresh_token
            ).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "Login", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/GetProfile", summary="获取当前用户信息")
async def get_profile(
    request: Request,
    current_user: dict = Depends(get_current_user)
):
    """获取当前登录用户信息"""
    try:
        return success(
            request, "GetProfile", SERVICE_NAME,
            ProfileResult(user=UserOut(**current_user)).model_dump()
        )
    except Exception as e:
        return error(
            request, "GetProfile", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/ChangePassword", summary="修改密码")
async def change_password(
    request: Request,
    body: ChangePasswordRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    修改当前用户密码
    
    **注意**：密码修改成功后，建议重新登录
    """
    try:
        # 查询用户
        user = await db.users.find_one({"_id": ObjectId(current_user["id"])})
        
        if not user:
            return error(
                request, "ChangePassword", SERVICE_NAME,
                ErrorCode.USER_NOT_FOUND
            )
        
        # 验证旧密码
        if not verify_password(body.old_password, user["hashed_password"]):
            return error(
                request, "ChangePassword", SERVICE_NAME,
                ErrorCode.INVALID_PASSWORD,
                message="旧密码不正确"
            )
        
        # 加密新密码
        new_hashed_password = hash_password(body.new_password)
        
        # 更新数据库
        await db.users.update_one(
            {"_id": ObjectId(current_user["id"])},
            {
                "$set": {
                    "hashed_password": new_hashed_password,
                    "updated_at": datetime.utcnow()
                }
            }
        )
        
        return success(
            request, "ChangePassword", SERVICE_NAME,
            ChangePasswordResult(
                message="密码修改成功，请重新登录"
            ).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "ChangePassword", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )


@router.post("/ChangeEmail", summary="修改邮箱")
async def change_email(
    request: Request,
    body: ChangeEmailRequest,
    current_user: dict = Depends(get_current_user),
    db: AsyncIOMotorDatabase = Depends(get_database)
):
    """
    修改当前用户邮箱
    
    **注意**：修改邮箱不会导致 Token 失效
    """
    try:
        # 检查新邮箱是否已被使用
        existing_user = await db.users.find_one({
            "email": body.new_email,
            "_id": {"$ne": ObjectId(current_user["id"])}
        })
        
        if existing_user:
            return error(
                request, "ChangeEmail", SERVICE_NAME,
                ErrorCode.EMAIL_ALREADY_EXISTS,
                message="该邮箱已被其他用户使用"
            )
        
        # 更新邮箱
        await db.users.update_one(
            {"_id": ObjectId(current_user["id"])},
            {
                "$set": {
                    "email": body.new_email,
                    "updated_at": datetime.utcnow()
                }
            }
        )
        
        return success(
            request, "ChangeEmail", SERVICE_NAME,
            ChangeEmailResult(
                message="邮箱修改成功",
                new_email=body.new_email
            ).model_dump()
        )
        
    except Exception as e:
        return error(
            request, "ChangeEmail", SERVICE_NAME,
            ErrorCode.SYSTEM_ERROR,
            detail=str(e)
        )

